# -*- coding: utf-8 -*-

import hashlib
import os
import pexpect

from fnmatch import fnmatch
from settings import FILE_IGNORE
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Unicode, UnicodeText, UniqueConstraint
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relation, scoped_session, sessionmaker
from utils import logfile

Base = declarative_base()

class Communication(Base):
    __tablename__ = "communication"

    id = Column(Integer, primary_key=True)
    host_id = Column(Integer, ForeignKey("hosts.id"), nullable=False)
    success = Column(Boolean, nullable=False)
    created_on = Column(DateTime, nullable=False)

    host = relation("Host", backref="hosts")

    def __init__(self, host, success, created_on):
        self.host = host
        self.success = success
        self.created_on = created_on

    def __repr__(self):
       return "<Communication('%s on %s')>" % (self.success, self.created_on)


class Crontab(Base):
    __tablename__ = "crontabs"

    hash = Column(String(32), primary_key=True)
    host_id = Column(Integer, ForeignKey("hosts.id"), nullable=False)
    contents = Column(Unicode, nullable=False)
    created_on = Column(DateTime, nullable=False)
    
    host = relation("Host", backref="crontabs")

    def __init__(self, hash, host, contents, created_on):
        if not isinstance(contents, unicode):
            contents = unicode(contents, "utf-8")
        self.hash = hash
        self.host = host
        self.host_id = host.id
        self.contents = contents
        self.created_on = created_on

    def __repr__(self):
       return "<Crontab('%s')>" % self.hash


class File(Base):
    __tablename__ = "files"

    id = Column(Integer, primary_key=True)
    host_id = Column(Integer, ForeignKey("hosts.id"), nullable=False)
    path = Column(Unicode, nullable=False)
    name = Column(Unicode)

    host = relation("Host", backref="files")

    def __init__(self, host, path, name):
        if not isinstance(path, unicode):
            path = unicode(path, "utf-8")
        if not isinstance(name, unicode):
            name = unicode(name, "utf-8")

        self.host = host
        self.host_id = host.id
        self.path = path
        self.name = name

    def __repr__(self):
       return "<File('%s/%s')>" % (self.path, self.name)
       
    def download(self):
        events = {"(?i)continue connecting": "yes\n", "(?i)password": "%s\n" % self.host.password}
        # The quotes around the file name below: http://blog.chilly.ca/?p=94
        pexpect.run("scp %s@%s:\"'%s/%s'\" %s/data/files/" % (self.host.username, self.host.ip, self.path, self.name, os.getcwd()), events=events, logfile=logfile(self.host.ip))

    def remove_local(self):
        file_dir, file_name = os.path.split(self.name)
        if os.path.exists("data/files/%s" % file_name):
            os.remove("data/files/%s" % file_name)


class FileMeta(Base):
    __tablename__ = "files_meta"

    id = Column(Integer, primary_key=True)
    file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
    hash = Column(String(32), nullable=False, unique=True)
    type = Column(String(1), nullable=False)
    links_to = Column(Unicode)
    permission = Column(String(9), nullable=False)
    owner = Column(Unicode, nullable=False)
    group = Column(Unicode, nullable=False)
    last_modified = Column(DateTime, nullable=False)
    size = Column(Integer)
    contents = Column(UnicodeText)
    contents_hash = Column(String(32))
    mime_type = Column(Unicode)
    crawled = Column(Boolean, default=False, nullable=False)

    file = relation("File", backref="files_meta")

    def __init__(self, file, type, links_to, permission, owner, group, last_modified):
        if not isinstance(links_to, unicode) and links_to is not None:
            links_to = unicode(links_to, "utf-8")
        if not isinstance(owner, unicode):
            owner = unicode(owner, "utf-8")
        if not isinstance(group, unicode):
            group = unicode(group, "utf-8")

        self.file = file
        self.file_id = file.id
        self.type = type
        self.links_to = links_to
        self.permission = permission
        self.owner = owner
        self.group = group
        self.last_modified = last_modified

    def __repr__(self):
       return "<FileMeta('%s/%s')>" % (self.file.path, self.file.name)
       
    def crawl(self):
        if self.type == "-":
            path_name = self.file.path + '/' + self.file.name
            matched = any(fnmatch(path_name, p) for p in FILE_IGNORE)
            if matched:
                return False
            self.file.download()
            self.hash_contents()
            self.file.remove_local()
            self.crawled = True
        elif self.type == "d":
            print "directory"
            #crawl_queue.put(self)
            #print crawl_queue.qsize() 

    def hash_contents(self):
        file_dir, file_name = os.path.split(self.file.name)

        if os.path.exists("data/files/%s" % file_name):
            f = os.popen("file -i data/files/'%s'" % file_name, "r")
            
            f_read = f.read()
            
            # f_read (file -i) samples:            
            # data/files/.bashrc: text/plain charset=us-ascii
            # data/files/.sudo_as_admin_successful: application/x-empty
            colon_position = f_read.find(":")
            mime_type = f_read[colon_position + 2:].split()[0]
            if not isinstance(mime_type, unicode):
                mime_type = unicode(mime_type, "utf-8")
            self.mime_type = mime_type
            
            is_text = f_read.find("text")

            fp = open("data/files/%s" % file_name)

            self.size = os.path.getsize("data/files/%s" % file_name)

            contents = []
            contents_hash = hashlib.md5()
            for line in fp:
                contents_hash.update(line)
                if is_text != -1:
                    contents.append(line)
            contents = "".join(contents)
            if not isinstance(contents, unicode):
                contents = unicode(contents, "utf-8")
            self.contents = contents
            self.contents_hash = contents_hash.hexdigest()


class FileAllow(Base):
    __tablename__ = "files_allow"

    id = Column(Integer, primary_key=True)
    file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
    allow = Column(Unicode)
    ignore = Column(Unicode)

    file = relation("File", order_by="File.name", backref="files_allow")

    def __init__(self, file_id, allow, ignore):
        self.file_id = file_id
        self.allow = allow
        self.ignore = ignore

    def __repr__(self):
        return "<FileAllow('%s allowing %s, ignoring %s')>" % (self.file.name, self.allow, self.ignore)


class FileDeny(Base):
    __tablename__ = "files_deny"

    file_id = Column(Integer, ForeignKey("files.id"), primary_key=True)

    file = relation("File", order_by="File.name", backref="files_deny")

    def __init__(self, file_id):
        self.file_id = file_id

    def __repr__(self):
        return "<FileDeny('%s')>" % self.file.name


class Host(Base):
    __tablename__ = "hosts"

    id = Column(Integer, primary_key=True)
    ip = Column(String(15))
    hostname = Column(Unicode)
    username = Column(Unicode)
    password = Column(Unicode)
    sudo = Column(Boolean, nullable=False)
    root_password = Column(Unicode)
    distro = Column(Unicode)
    version = Column(String)
    scan_every = Column(Integer)

    def __init__(self, ip, hostname=None, username=None, sudo=False, password=None, root_password=None, distro=None, version=None, scan_every=None):
        self.ip = ip
        self.hostname = hostname
        self.username = username
        self.password = password
        self.sudo = sudo
        self.root_password = root_password
        self.distro = distro
        self.version = version
        self.scan_every = scan_every

    def __repr__(self):
       return "<Host('%s')>" % self.ip


class Resource(Base):
    __tablename__ = "resources"

    id = Column(Integer, primary_key=True)
    host_id = Column(Integer, ForeignKey("hosts.id"), nullable=False)
    free_disk_space = Column(Integer, nullable=False)
    free_memory = Column(Integer, nullable=False)
    swap = Column(Integer, nullable=False)
    processor = Column(Integer, nullable=False)
    created_on = Column(DateTime, nullable=False)
    
    host = relation("Host", backref="resources")

    def __init__(self, host, free_disk_space, free_memory, swap, processor, created_on):
        self.host = host
        self.host_id = host.id
        self.free_disk_space = free_disk_space
        self.free_memory = free_memory
        self.swap = swap
        self.processor = processor
        self.created_on = created_on

    def __repr__(self):
       return "<Resource('%s')>" % self.id


class Tag(Base):
    __tablename__ = "tags"

    id = Column(Integer, primary_key=True)
    file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
    tag = Column(Unicode, nullable=False)
    
    file = relation("File", backref="tags")
    
    def __init__(self, file, tag):
        if not isinstance(tag, unicode):
            tag = unicode(tag, "utf-8")
        self.file = file
        self.file_id = file.id
        self.tag = tag

    def __repr__(self):
       return "<Tag('%s')>" % self.tag


def create_all(engine):
    Base.metadata.create_all(engine)
